Scripting Language (appendix B)

The GrADS scripting language, used via the GrADS run command, provides a similar capability to the exec command, except that a script may have variables, flow control, and access GrADS command output. Scripts may be written to perform a variety of functions, such as allowing a user to point and click on the screen to select something, to animate any desired quantities, to annotate plots with information obtained from GrADS query commands.

Overview of the Scripting Language

The scripting language is similar to REXX in implementation. All variables are of type STRING. Operations are supported on script variables. Flow control is acheived via if/else/endif and while/endwhile constructs. Loop flow may be modified by the continue or break commands. Strings contained in variables or generated via an expression may be issued to GrADS as commands. The result of those commands (the string that GrADS would have typed on the terminal) is put into a variable and made available to the script. The language includes support for functions.

Elements of the Language

A script file is split into records. The end of a script record is determined by either a newline character (end of record for the file) or a semicolon (where the semicolon is not contained within a constant string).

Each script record may be one of the following script record types:

      Assignment
     If / Else / Endif
     while / endwhile / break / continue
     function header / return
     say / pull

If a script record is none of the above, it is assumed to be an statement record, which contains a script expression. The result of the expression is passed to GrADS as a command for execution.

Many of the above record types will contain expressions. Script expression are composed of operators and operands, where the operands are script variables, function calls, or constants, and the operators are mathematical, logical, or concatenation operations.

There is no 'goto' in this language.

Variables

Script Language variable names are 1 to 8 characters, beginning with an alphabetic character and containing letters or numbers only. The name is case sensitive.

The contents of a script variable is always a character string. For some operations, the character string will be interpreted as a number.

If a variable has not yet been assigned, its value is its name.

If the contents of a variable or string constant are a number in the correct format, certain operators will perform numeric operations, giving a string result which will also be a number.

Two variable names are predefined: 'result' and 'rc'. It is a good idea to avoid assigning values to these variables.

Compound scripting variables - The scripting language now supports compound variables, which can be used to construct arrays in scripts. A compound variable has a variable name with segments seperated by periods. For example:

                			varname.i.j

In this case, when the variable contents are accessed, i and j will be looked up to see if they are also variables (non-compound). If they are, the i and j will be replaced by the string values of i and j.

For example:

                  			i = 10
                  			j = 3
                  			varname.i.j = 343

In the above example, the assignment is equivalent to:

                  			varname.10.3 = 343

Note that the string values of i and j may be anything, but the variable name specification in the script must follow the rules for variable names: letters or numbers, with a leading letter. The variable name after substitution may be any string:

                   			i = 'a#$xx'
                   			varname.i = 343

The above is valid. However, we cannot refer to this variable name directly:

                   			varname.a#$xx = 343

would be invalid.

Variable names MAY NOT be longer than 16 characters, either BEFORE OR AFTER substitution.

Note that the GrADS scripting language is not particularly efficient in handling large numbers of variables. Thus compound variables should not be used to create large arrays:

                  			i = 1
                  			while (i<10000)
                    			     var.i = i
                  			endwhile

The above loop will create 10000 distinct variable names. Having that number of variables in the variable chain will slow the script down a lot. If this turns out to be a poor design choice, let me know and I will consider making the variable handling more efficient.

Operators

The following operators are implemented:

       |  	logical or
       &   	logical and
       =  	equality
       !=  	not equal
       >   	greater than
       >=  	greater than or equal
       <   	less than
       <=  	less than or equal
       %   	concatenation
       +  	addition
       -   	subtraction
       *   	multiplication
       /   	division
       !   	unary not
       -   	unary minus

The following operators will perform a numeric operation if the operands are numeric:

       =, !=, >, >=, <, <=, +, -, *, /

If any of the following operations are attempted with non-numeric operands, an error will result:

       +, -, *, /

Arithmetic operations are done in floating point. If the result is integral, the result string will be integer.

A logical operator will give a character 0 if the result is false, and a character 1 if the result is true.

Expressions

Script expressions consist of operands, operators, and parentheses.

The precedence of operators is:

       -, !  (Unary)
       /, *
       +, -
       %
       =, !=, >, >=, <, <=    
       & 
       |

Within the same precedence level, operations are performed left to right.

Operands may be variables (discussed earlier), string constants, or function calls. String constants are enclosed in either single or double quotes. Numeric constants may be entered without quotes -- but are still considered string constants.

An example of a string constant:

       'This is a string'

The entire expression, including all function calls, etc. will be performed to obtain a result. For example:

       var1!='' & var1*var2<10

In this expression, both sides of the logical AND operation will be resolved, and the subexpression to the right might result in an error. In these cases, a double nested if will be required.

In some cases, the concatenation operator is implied. This takes place whenever two operands abut (with or without intervening blanks -- the blanks are ignored).

For example, the following expressions are identical:

      var1%var2%'String'
      var1 var2'String'

Keep in mind the order of precedence for the concatenation operator.

Parentheses modify the order of operation in the expected ways.

Function calls take the form of:

      name(arg,arg,arg,...)

where the name follows the same rules as for variable names, and the args may be expressions.

IF Blocks

Flow of control may be controlled via the if/else/endif construct. The format is:

      if expression                    Must be on seperate record
         script record
         script record
           .
           .
       else                             Optional
         script record
           .   
           .
       endif                            Required!

Note that the following script record is invalid:

         if (i=10) j=20

You would instead need to enter three script records:

if (i=10)

j = 20

endif

You could enter these three script records on the same line:

          if (i=10); j=20; endif;

The portion of the if block executed depends on the result of the expression. If the expression resolves to a string containing the character 0, the 'else' portion is executed. If the result string is ANYTHING else, the 'if' portion is executed.

WHILE Blocks

The while contstruct is as follows:

while expression On seperate script record

script record

script record

.

.

endwhile Required!

While the expression is true -- ie, is not exactly equal to a character 0 -- the loop is executed.

Two additional script commands can be used to modify the loop execution. break will end execution of the loop immediately. continue will branch immediately back to the top of the loop, and the expression will be re-evaluated.

For example:

     t = 1
     while (t<10)
       'set t 't
       'display z'
       if (rc!=0); break; endif;
       t = t + 1
     endwhile

Functions

Functions may either be contained within the script file itself, or the may be intrinsic functions. Functions contained within other script files are not supported as yet (other script files may be executed via the GrADS run command).

In either case, functions are invoked as a script expression is being evaluated. Script functions always have a single string result, but may have one or more string arguments. Functions are invoked by:

      name(arg,arg,arg...)

If the function has no arguments, you must still provide the parentheses:

      name()

You may provide your own functions from within your script file by using the function definition record:

      function name(variable, variable, ...)

To return from a function, use the return command:

      return expression        

The expression is option; if not provided, a NULL string will be returned. (A null string is: '') The result of the function is the result of the expression specified on the return command.

When a function is invoked, the arguments are evaluated, then flow of control is transfered to the function. The variables contained in the list within the function definition record are initialized to the values of the passed arguments. If too few arguments where passed for the variables specified, the trailing variables are uninitialized. If too many arguments are passed, the extra arguments are discarded.

You may modify the variables from the function definition record without modifying the variables from the calling routine.

Scope of variables is normally local to the function, but can be global.

When a script file is first invoked (via the run command), execution starts at the beginning of the file. A function definition record may optionally be provided at the beginning. If it is, it should specify one variable name. This variable will be initialized to any 'run' command options. If no options were given, the variable will be initialized to NULL.

Assignment

The format of the assignment record is:

    variable = expression

The expression is evaluated, and the result is assigned to be the value of the indicated variable.

Interacting With the User (Say, Pull)

The 'say' command is:

     say expression

The result of the expression is written to the terminal.

The pull command is:

     pull variable

The script pauses for user keyboard input (up to the carraige return), and the string entered by the user is assigned to the indicated variable name.

Sending Commands to GrADS

The statement record consists only of an expression:

expression

The expression is evaluated, and the resulting string is submitted to GrADS as a command.

After this record is executed, the script variable 'result' is given the value of the result of the GrADS command (the result in this case is the string that GrADS would have typed to the terminal had you entered the command interactively). The script variable 'rc' is given the return code from the GrADS command (this will always be an integer value).

The result may contain several GrADS output lines. These will be concatenated into one long string, and can be seperated in the script using the 'sublin' function.

A GrADS error resulting from an invalid command WILL NOT terminate execution of the script.

You may issue any GrADS commands from the scripting environment, including the run command. The result string from issuing the run command will be the string passed back from that 'lower level' script via the 'return' command in that script -- when that script returns to GrADS (and thus returns to the higher level script). You may recursively call any script, but you are responsible for ensuring that you can get back out of the recursion.

Intrinsic Functions

Only a few intrinsic (built in) functions are as yet implmented:

      substr (string, start, length)

The sub-string of string starting at location 'start' for length 'length' will be returned. If the string is too short, the result will be short or NULL. 'start' and 'length' must be integer string values.

      subwrd (string, word)

The result is the nth 'word' from the string. If the string is too short, the result is NULL. 'word' must be integer.

      sublin (string, line)

The result is the nth 'line' from the string. If the string has too few lines, the NULL string is returned. 'line' must be integer.

      read (name)

The next record from file 'name' is read. Repeated calls may be made to read consecutive records. The result is two lines within one string. The first line is the return code, the 2nd line is the record read. The record may be a maximum of 80 characters. Use the 'sublin' function to seperate the result. Return codes are:

0 - ok

1 - open error

2 - end of file

8 - file open for write

9 - I/O error

Files are opened when the first call to read is made for a particular file name. Files are closed when the execution of the script file terminates (note that files remain open between function calls, etc).

     write (name, record <, append>)

The record is written to file 'name'. On the first call to write for a particular file, the file is opened in write mode. This will destroy an existing file! If you use the optional append flag, the file will be opened in append mode, and all writes will be appended to the end of the file.

Return codes are:

0 - ok

1 - open error

8 - file open for read

      close (name)

Closes the named file. This must be done if you wish to read from a file you have been writing to. This can also be used to rewind a file. Return codes:

0 - ok

1 - file not open

Examples:

A few simple example scripts are provided with the GrADS distribution. If you do not know where these files are, send me email (doty@cola.umd.edu) and I will send them to you. I can also send you other (longer) examples which require data sets that you won't have, but which can provide more complex examples.

cbarn.gs - Draw color bars after a shaded contour plot is displayed

xyplot.gs - Does general XY plot.

string.gs - Plots string at point-click location.

draw.gs - Draw line via point-click.

wxsym.gs- display weather symbols

font.gs - demonstration of fonts